/*
Inserire gli include necessari
*/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>


/*
Dichiarare le seguenti funzioni:
- "inserisci_matrice" per permettere all'utente di inserire a mano
	i valore della matrice
- "genera_matrice" per fare in modo che la matrice venga generata
	in modo random
- "stampa_matrice" per stampare il contenuto della matrice
*/
void inserisci_matrice(int **m, unsigned int rank);
void genera_matrice(int **m, unsigned int rank, unsigned int max_value);
void stampa_matrice(int **m, unsigned int rank);

int main(int argc, char* args[])
{	

	/*
	Dichiarare le variabili necessarie
	Ricordare che la matrice deve essere allocata
	in modo dinamico, pertanto dovra' essere dichiarata
	come un puntatore di puntatori a interi.
	*/
	unsigned int rank, i, j, max_value;
	int answer, tmp, **m;


	/*
	Inserire tutto il codice all'interno di un ciclo che permetta
	di gestire il menu. Dal programma si deve uscire solo se 
	l'utente sceglie l'opzione 0.
	Controllare che la scelta effettuata dall'utente sia corretta.	
	*/
	do{
		printf("\n\n*********************\n");
		printf("1 INSERIMENTO MANUALE\n");
		printf("2 GENERAZIONE RANDOM\n");
		printf("0 USCITA\n");
		printf("Seleziona: ");
		
		scanf("%d", &answer);
		if(answer != 0 && answer != 1 && answer != 2){
			printf("Comando errato\n");
			continue;
		}
		if(answer == 0)
			break;
		
		/*
		Leggere da tastiera il rango della matrice. Se il valore
		inserito non e' maggiore di uno terminare il programma
		con un errore.
		*/
		printf("Inserire il rango della matrice: ");
		scanf("%u", &rank);
				
		/* 
		Allocare la memoria per la matrice.
		Prima allocare una colonna di puntatori
		a interi. Ogni elemento della colonna
		sara' a sua volta un puntatore a interi e servira' per 
		puntare allo spazio che conterra' una riga di interi
		*/
		m = (int **) malloc(sizeof(int *) * rank);
		for(i = 0; i < rank; ++i)
			m[i] = (int *) malloc(sizeof(int) * rank);
	
		/*
		Se l'utente ha scelto l'opzione 1, chiamare la
		funzione "inserisci_matrice" per l'inserimento manuale dei
		dati nella matrice.
		Altrimenti se l'utente ha scelto l'opzione 2, chiamare
		la funzione "genera_matrice" per la generazione random
		dei valori. In questo caso bisogna anche leggere da tastiera
		il valore massimo che potra' essere contenuto negli elementi
		della matrice. Per esempio, l'utente potrebbe decidere che
		il valore massimo e' 50, pertanto ogni elemento della matrice
		dovra' essere contenuto nell'intervallo [0,50].
		Questo valore dovra' essere fornito come parametro alla funzione
		"genera_matrice", assieme a puntatore alla matrice.
		*/
		switch(answer){
			case 1:
				inserisci_matrice(m, rank);
				break;
			case 2:
				printf("Inserisci il valore massimo: ");
				scanf("%u", &max_value);
				genera_matrice(m, rank, max_value);
				break;
			default:
				exit(1);
		}
				
							
		/*
		Stampare il contenuto della matrice chiamando la funzione
		stampa_matrice.
		*/
		stampa_matrice(m, rank);
	
		/*
		Calcolare la trasposta della matrice
		*/
		for(i = 0; i < rank; ++i){
			for(j = 0; j < i; ++j){
				tmp = m[i][j];
				m[i][j] = m[j][i];
				m[j][i] = tmp;
			}
		}
				
	
		/*
		Stampare il contenuto della matrice trasposta chiamando la funzione
		stampa_matrice.
		*/
		printf("Inversa della matrice: \n");
		stampa_matrice(m, rank);
		
		/* liberare la memoria allocata */
		for(i = 0; i < rank; ++i)
			free(m[i]);
		free(m);
		
	}while(answer != 0);
	
	return 0;
}


/*
Definire la funzione "genera matrice" per la 
generazione random dei valori della matrice.
Per generare un numero random compreso tra 0 e N
bisogna:
1) inizializzare con la funzione srand() il generatore di
numeri casuali. La chiamata da effettuare e':
srand(time(NULL));
vedere il man di srand() e di time().
2) usare la funzione rand() per ottenere un numero
casuale compreso tra 0 e RAND_MAX (RAND_MAX e' una costante
del C che contiene il massimo valore generabile random).
Vedere il man di rand()
3) dividire il numero generato da rand() per RAND_MAX
in modo da ottenere un numero tra 0 e 1. Moltiplicare, quindi
il risultato per il valore N in modo da ottenere un numero
compreso tra 0 e N.
*/
void genera_matrice(int **m, unsigned int rank, unsigned int max_value){
	unsigned int i,j;
	
	srand(time(NULL));
	
	for(i = 0; i < rank; ++i)
		for(j = 0; j < rank; ++j)
			m[i][j] = (int)( ((float) rand())/RAND_MAX*max_value);
}

/*
Definire la funzione "inserisci_matrice".
In questo caso i valori della matrice devono essere
letti da tastiera uno alla volta.
*/
void inserisci_matrice(int **m, unsigned int rank){
	unsigned int i, j;
	
	for(i = 0; i < rank; ++i){
		for(j = 0; j < rank; ++j){
			printf("m[%u][%u]=", i, j);
			scanf("%d", &m[i][j]);
		}
	}
}


/*
Definire al funzione "stampa_matrice" per permettere
la stampa a video di una matrice di dimensione n.
*/
void stampa_matrice(int **m, unsigned int rank){
	unsigned int i, j;
	
	for(i = 0; i < rank; ++i){
		for(j = 0; j < rank; ++j){
			printf("%d\t", m[i][j]);
		}
		printf("\n");
	}
}